package com.yth.utils;

import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.PropertiesConfigurationLayout;
import org.apache.commons.configuration2.ex.ConfigurationException;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

/**
 * 用于读写属性文件并保留注释及原始格式的工具类，提供属性增删改查和链式操作支持。
 * 注意：本类非线程安全，多线程环境下需外部同步。
 *
 * @author yutianhong
 * @version 1.0
 * @since 2025/4/14 16:12
 */
public class PropertiesProcessor {
    private final PropertiesConfiguration config;
    private final PropertiesConfigurationLayout layout;

    private PropertiesProcessor(Reader reader) throws ConfigurationException {
        config = new PropertiesConfiguration();
        layout = new PropertiesConfigurationLayout();
        config.setLayout(layout);
        config.setIOFactory(new PropertiesConfiguration.JupIOFactory(false));   // 禁用unicode转义
        // 加载配置（保留注释和格式）
        layout.load(config, reader);
    }

    /**
     * 从reader中读取并创建PropertiesProcessor实例
     */
    public static PropertiesProcessor fromReader(Reader reader) {
        if (reader == null) {
            throw new IllegalArgumentException("reader is null");
        }
        try {
            return new PropertiesProcessor(reader);
        } catch (Throwable e) {
            throw new ConfigurationRuntimeException("加载属性配置失败", e);
        }
    }

    /**
     * 从指定属性字符串创建PropertiesProcessor实例
     */
    public static PropertiesProcessor fromContent(String propertiesString) {
        try (StringReader reader = new StringReader(propertiesString != null ? propertiesString : "")) {
            return new PropertiesProcessor(reader);
        } catch (Throwable e) {
            throw new ConfigurationRuntimeException("加载属性配置失败", e);
        }
    }

    /**
     * 从指定文件路径加载属性并创建PropertiesProcessor实例
     */
    public static PropertiesProcessor fromFilePath(String filePath) {
        if (filePath == null) {
            throw new IllegalArgumentException("filePath is null");
        }
        try (InputStreamReader reader = new InputStreamReader(
                Files.newInputStream(Paths.get(filePath)), StandardCharsets.UTF_8)) {
            return new PropertiesProcessor(reader);
        } catch (Throwable e) {
            throw new ConfigurationRuntimeException("加载属性文件失败: " + filePath, e);
        }
    }

    /**
     * 创建一个空的PropertiesProcessor实例
     */
    public static PropertiesProcessor empty() {
        return fromContent("");
    }

    /**
     * 根据Map中的键值对创建PropertiesProcessor实例并设置属性
     */
    public static PropertiesProcessor fromMap(Map<String, String> map) {
        PropertiesProcessor processor = empty();
        if (map != null) {
            map.forEach(processor::setProperty);
        }
        return processor;
    }

    /**
     * 从指定文件路径加载属性并创建PropertiesProcessor实例
     */
    public static PropertiesProcessor fromProperties(Properties properties) {
        PropertiesProcessor processor = empty();
        if (properties != null) {
            properties.forEach((k, v) ->
                    processor.setProperty(
                            k.toString(),
                            v != null ? v.toString() : null
                    )
            );
        }
        return processor;
    }

    /**
     * 将当前配置转换为保留格式的属性字符串
     */
    public String toPropertiesString() {
        try (StringWriter writer = new StringWriter()) {
            layout.save(config, writer);
            return writer.toString();
        } catch (ConfigurationException e) {
            throw new ConfigurationRuntimeException("生成属性字符串失败", e);
        } catch (IOException e) {
            // should not reach here
            throw new RuntimeException("StringWriter 不应抛出 IOException", e);
        }
    }

    /**
     * 将当前配置转换为Map，键为属性名，值为对应的最后一个属性值。
     */
    public Map<String, String> toMap() {
        int size = calculateMapInitialCapacity(size());
        Map<String, String> map = new LinkedHashMap<>(size);
        for (String key : keySet()) {
            String value = getProperty(key);
            if (value != null) {
                map.put(key, value);
            }
        }
        return map;
    }

    /**
     * 将当前配置转换为标准的Properties对象。
     */
    public Properties toProperties() {
        Properties properties = new Properties();
        for (String key : keySet()) {
            String value = getProperty(key);
            if (value != null) {
                properties.setProperty(key, value);
            }
        }
        return properties;
    }

    /**
     * 设置指定键的值
     */
    public PropertiesProcessor setProperty(String key, String value) {
        config.setProperty(key, value);
        return this;
    }

    /**
     * 批量设置Map中的所有键值对
     */
    public PropertiesProcessor setProperties(Map<String, String> map) {
        if (map != null) {
            map.forEach(this::setProperty);
        }
        return this;
    }

    /**
     * 添加指定键的值（允许多值）
     */
    public PropertiesProcessor addProperty(String key, String value) {
        config.addProperty(key, value);
        return this;
    }

    /**
     * 获取指定键的最后一个值，若不存在返回null
     */
    public String getProperty(String key) {
        Object property = config.getProperty(key);
        if (property == null) {
            return null;
        } else if (property instanceof String) {
            return (String) property;
        } else if (property instanceof List) {
            List<?> propertyList = (List<?>) property;
            // get last one
            if (propertyList.isEmpty()) {
                return null;
            }
            Object lastProperty = propertyList.get(propertyList.size() - 1);
            return (String) lastProperty;
        }
        // should not reach here
        throw new IllegalStateException("键 '" + key + "' 的属性类型应为字符串或字符串列表");
    }

    /**
     * 获取指定键的所有值列表，可能为空列表
     */
    public List<String> getPropertyValues(String key) {
        Object property = config.getProperty(key);
        if (property == null) {
            return Collections.emptyList();
        } else if (property instanceof String) {
            return Collections.singletonList((String) property);
        } else if (property instanceof List) {
            //noinspection unchecked
            List<String> propertyValues = (List<String>) property;
            return Collections.unmodifiableList(propertyValues);
        }
        // should not reach here
        throw new IllegalStateException("键 '" + key + "' 的属性类型不合法: " + property.getClass());
    }

    /**
     * 返回当前配置中的键的数量
     */
    public int size() {
        return config.size();
    }

    /**
     * 删除指定键及其所有值
     */
    public PropertiesProcessor removeProperty(String key) {
        config.clearProperty(key);
        return this;
    }

    /**
     * 获取所有键的不可修改有序集合
     */
    public Set<String> keySet() {
        Set<String> keys = new LinkedHashSet<>();
        config.getKeys().forEachRemaining(keys::add);
        return Collections.unmodifiableSet(keys);
    }

    /**
     * 清空所有属性
     */
    public PropertiesProcessor clear() {
        config.clear();
        return this;
    }

    /**
     * 获取指定键的注释，若不存在返回null
     */
    public String getComment(String key) {
        return layout.getComment(key);
    }

    /**
     * 为指定键设置注释
     */
    public PropertiesProcessor setComment(String key, String comment) {
        layout.setComment(key, comment);
        return this;
    }

    /**
     * 获取属性文件的头部注释，若不存在返回null
     */
    public String getHeaderComment() {
        return layout.getHeaderComment();
    }

    /**
     * 设置属性文件的头部注释
     */
    public PropertiesProcessor setHeaderComment(String comment) {
        layout.setHeaderComment(comment);
        return this;
    }

    /**
     * 获取属性文件的底部注释，若不存在返回null
     */
    public String getFooterComment() {
        return layout.getFooterComment();
    }

    /**
     * 设置属性文件的底部注释
     */
    public PropertiesProcessor setFooterComment(String comment) {
        layout.setFooterComment(comment);
        return this;
    }

    /**
     * 获取规范化的头部注释
     */
    public String getCanonicalHeaderComment() {
        return layout.getCanonicalHeaderComment(false);
    }

    /**
     * 获取规范化的底部注释
     */
    public String getCanonicalFooterComment() {
        return layout.getCanonicalFooterCooment(false);
    }

    /**
     * 获取指定键的规范化注释
     */
    public String getCanonicalComment(String key) {
        return layout.getCanonicalComment(key, false);
    }

    /**
     * 设置指定键前的空行数
     */
    public PropertiesProcessor setBlankLinesBefore(String key, int lines) {
        layout.setBlankLinesBefore(key, lines);
        return this;
    }

    /**
     * 获取指定键前的空行数
     */
    public int getBlankLinesBefore(String key) {
        return layout.getBlankLinesBefore(key);
    }

    /**
     * 获取全局分隔符
     */
    public String getGlobalSeparator() {
        return layout.getGlobalSeparator();
    }

    /**
     * 设置属性键值对之间的全局分隔符。如果设置为null，代表默认行为。
     */
    public PropertiesProcessor setGlobalSeparator(String separator) {
        layout.setGlobalSeparator(separator);
        return this;
    }

    /**
     * 获取属性键值对的分隔符
     */
    public String getSeparator(String key) {
        return layout.getSeparator(key);
    }

    /**
     * 设置指定属性键值对的分隔符
     */
    public PropertiesProcessor setSeparator(String key, String separator) {
        layout.setSeparator(key, separator);
        return this;
    }

    private int calculateMapInitialCapacity(int expectedSize) {
        if (expectedSize == 0) {
            return 2;
        }
        int maximumCapacity = 1 << 30;
        float ft = ((float) expectedSize / 0.75f) + 1.0F;
        return ((ft < (float) maximumCapacity) ? (int) ft : maximumCapacity);
    }

    public static class ConfigurationRuntimeException extends RuntimeException {
        ConfigurationRuntimeException(String message, Throwable cause) {
            super(message, cause);
        }
    }
}
